
package jogs.net.gameroomserver; 

import jogs.core.*;
//import jogs.net.metaserver.MetaServer.GameRoomServerInfo;
//import jogs.games.Game;

import java.util.*;
import java.net.*;
import java.io.*;
import java.sql.*;

/**
 * A Game Room Server acts as a liason for everything related to a particular
 * game room. The idea is to have one game room per host dedicated to the
 * particular game (eg. Tic Tac Toe). 
 */

public class GameRoomServer
{
  private Properties settings; 
  private int port; 
  private ServerSocket serverSocket;

  private ArrayList<GRSClientHandler> clients;
  private GRSClientHandler metaServer; 
  
  private HashMap<String,String> userpasses;

  private HashMap<String,TableServerInfo> tag2tsi;
  
  private String name; 

  private String mspw, grspw;

  private String dbhost, dbuser, dbpass, dbname; 
  private Connection dbconn;
  
  /** 
   * Static methods
   */

  private static void exit_usage()
  {
    System.out.println("Usage: GameRoomServer <settings file>"); 
    System.exit(-1); 
  }

  public static void main(String[] args)
  {
  	System.out.println("starting");
    if (args.length < 1) 
      exit_usage(); 
    System.out.println("starting");
    GameRoomServer grs = new GameRoomServer(args[0]); 
    grs.start(); 
  }
  
  /**
   * Class Methods
   */ 
  
  public GameRoomServer(String propFile)
  {
    clients = new ArrayList<GRSClientHandler>(); 
    userpasses = new HashMap<String,String>(); 
    parseSettings(propFile);     
    tag2tsi = new HashMap<String,TableServerInfo>(); 
  }

  public synchronized void setMetaServer(GRSClientHandler ms)
  {
  	metaServer = ms;
  }
  
  public synchronized String getName()
  {
  	return name;
  }
  
  public synchronized void addClient(GRSClientHandler newClient)
  {
  	String allinfo = "@ALLINFO:";
  	for(TableServerInfo tsi : this.tag2tsi.values())
  	{
  		allinfo += "@TABLE;" + tsi.getInfoString() + ":";
  	}
  	for(GRSClientHandler client : clients )
  	{
  		allinfo += "@USER;" + client.getUsername() + ":";
  	}
  	newClient.send(allinfo);
  	this.clients.add(newClient);
  	broadcast("@ENTERED:" + newClient.getUsername()); 
  	if(metaServer != null)
  		this.metaServer.send("@CLIENT_COUNT:" + this.getClientCount());
  	
  }
  
  public synchronized void broadcast(String msg)
  {
    for (GRSClientHandler client : clients)
      if (!client.disconnected()) client.send(msg); 
  }

  public synchronized void addKeyPass(String username, String keypass)
  {
    userpasses.put(username, keypass); 
  }
  
  public synchronized int getClientCount()
  {
    return clients.size(); 
  }
 
  // Handles incoming join request from metaserver
  public String handleJoin(String line, String username)
  {
    String[] parts = line.split(" "); 
    String room = parts[1]; 
    String info = null;

    for (TableServerInfo tsi : tag2tsi.values())
    {
      if (room.equals(tsi.name))
      {
        String keypass = Helpers.genKeyPass(128); 
        try
        {
          tsi.out.println("ADDKEYPASS " + username + " " + keypass); 
          info = room + " " + tsi.host + " " + tsi.port + " " + keypass; 
          //String response = tsi.in.readLine();
       
          // Wait for response
          tsi.waitForCommand();  
          String response = tsi.getLastCommand(); 
          if (!response.equals("OK ADDKEYPASS"))
          {
            System.out.println("GRS returned: " + line);
            return null; 
          }
        }
        catch(Exception e)
        {
          e.printStackTrace();
        }

        break;
      }
    }
    return info;
  }

  // Handles incoming sit request from client 
  public String handleSit(String table, String username)
  {
    String info = null;

    // look for a table by that name

    for (TableServerInfo tsi : tag2tsi.values())
    {
      if (table.equals(tsi.name))
      {
        String keypass = Helpers.genKeyPass(128); 
        try
        {
          tsi.out.println("@ADDKEYPASS:" + username + "," + keypass); 
          info = table + "," + tsi.host + "," + tsi.port + "," + keypass; 
         //String response = tsi.in.readLine();
       // Wait for response
          tsi.waitForCommand();  
          String response = tsi.getLastCommand(); 
          if (!response.equals("@OK_ADDKEYPASS"))
          {
            System.out.println("TS returned: " + response);
            return null;
          }
        }
        catch(Exception e)
        {
          e.printStackTrace();
        }

        break;
      }
    }

    return info; 
  }

  public int authenticate(String username, String password)
  {
    int ret = 0; 

    try 
    { 
      System.out.println("Authenticating... "); 

      // Check if it's the metaserver
      
      if (username.equals("#@metaserver@#") && password.equals(mspw))
        return 1; 

      // Check the lease list
      
      String keypass = userpasses.get(username);
      if (keypass == null)
        return 0; 

      if (password.equals(keypass))
        return 2;

      return 0; 
    }
    catch(Exception e)
    {
      e.printStackTrace(); 
    }

    return 0;
  }

  public synchronized void leave(GRSClientHandler client)
  {
	  this.clients.remove(client);
	  broadcast("@LEFT:" + client.getUsername());
	  if(metaServer != null)
  		this.metaServer.send("@CLIENT_COUNT:" + this.getClientCount());
  }
  
  private String getSetting(String key)
  {
    String setting = settings.getProperty(key);

    if (setting == null)
     Helpers.fatalError("Required setting missing in settings: " + key); 

    return setting; 
  }

  private void parseSettings(String propFile)
  {
    try
    {
      System.out.println("Parsing settings...");
      FileInputStream in = new FileInputStream(propFile); 
      settings = new Properties(); 
      settings.load(in); 

      port = Integer.parseInt(settings.getProperty("port")); 

      //dbhost = getSetting("dbhost"); 
      //dbuser = getSetting("dbuser"); 
      //dbpass = getSetting("dbpass"); 
      //dbname = getSetting("dbname"); 

      mspw = settings.getProperty("mspw"); 
      grspw = settings.getProperty("grspw"); 

      name = settings.getProperty("name"); 

    }
    catch(Exception e)
    {
      Helpers.fatalError(e); 
    }
  }

  private void dbconnect()
  {
    try
    {
      String url = "jdbc:mysql://" + dbhost + "/" + dbname;
      Class.forName("com.mysql.jdbc.Driver").newInstance();
      dbconn = DriverManager.getConnection(url, dbuser, dbpass);
      System.out.println ("Database connection established");
    }
    catch(Exception e)
    { 
      Helpers.fatalError(e);
    }
  }
    
  public void updateTableInfo(String info)
  {
  	String update = "@UPDATETABLE:" + info;
  	
  	this.broadcast(update);
  }
  
  private void connect_ts()
  {
    Enumeration keys = settings.propertyNames(); 

    while (keys.hasMoreElements())
    {
      String key = (String)keys.nextElement();  

      if (!key.startsWith("tserver."))
        continue; 

      String tag = key.substring(9); 
      String value = settings.getProperty(key);
      String[] parts = value.split(":"); 
      String host = parts[0];
      int port = Integer.parseInt(parts[1]); 

      System.out.print(tag + " " + host + ":" + port + " ... "); 
      
      try
      {
        Socket socket = new Socket(host, port); 
        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
        PrintStream out = new PrintStream(socket.getOutputStream()); 

        in.readLine(); // AUTHENTICATE

        out.println("#@gameroomserver@#"); 
        out.println(grspw); 

        String response = in.readLine();
        System.out.print(response); 

        if (!response.equals("@LOGINSUCC:"))
        {
          System.out.println("");
          continue;
        }
          
        System.out.println("");

        out.println("@GET_CLIENT_COUNT:");
        int clientCount = Integer.parseInt(in.readLine()); 
        
        out.println("@GET_NAME:");
        String name = in.readLine(); 

        //System.out.println(" " + name + " " + clientCount);

        TableServerInfo tsi = new TableServerInfo(host, port, socket, in, out, name, clientCount, this); 
        tag2tsi.put(tag, tsi); 
        tsi.start();
      }
      catch(Exception e)
      {
        System.out.println(e);
      }
    }
  }

  private void start()
  {
    try 
    {
      //System.out.println("Connecting to database."); 
      //dbconnect();

      System.out.println("Connecting to table servers...");
      connect_ts();
      if (tag2tsi.size() < 1)
         Helpers.fatalError("Couldn't connect to any table servers. Exiting..."); 

      System.out.println("Binding socket to port " + port); 
      serverSocket = new ServerSocket(port); 

      System.out.println("Accepting clients..."); 

      while(true)
      {
        Socket client = serverSocket.accept(); 

        System.out.println("Client accepted. Creating and starting client handler. "); 

        GRSClientHandler grscl = new GRSClientHandler(client, this, false); 
        grscl.start(); 
        /*if(metaServer != null)
        	this.metaServer.send("@CLIENT_COUNT:" + this.getClientCount());*/
      }
    }
    catch(Exception e)
    {
      e.printStackTrace(); 
    }
  }
}

